/**
 * 
 */
package biz.innovasoft.nodb.impl;

import java.io.Serializable;
import java.util.List;

import biz.innovasoft.nodb.Repository;
import biz.innovasoft.nodb.RepositoryException;
import biz.innovasoft.nodb.index.IndexException;
import biz.innovasoft.nodb.index.IndexManager;
import biz.innovasoft.nodb.index.Indexer;
import biz.innovasoft.nodb.index.RepositoryIndexManager;
import biz.innovasoft.nodb.index.Searcher;
import biz.innovasoft.nodb.index.SearcherResult;
import biz.innovasoft.nodb.query.QueryCriteria;
import biz.innovasoft.nodb.query.Result;
import biz.innovasoft.nodb.query.Tuple;
import biz.innovasoft.nodb.util.IndexUtils;

/**
 * 
 * @author Diego Miranda
 *
 */
public class FindHelper implements Serializable {

	private static final long serialVersionUID = 1102956905193518896L;
	
	private Repository repository;
	private Searcher searcher;
	private RepositoryIndexManager temporal = new RepositoryIndexManager();
	
	private IndexManager index;
		
	public FindHelper (GenericRepository repository) {
		this.repository = repository;
	}
	
	public FindHelper (Repository repository, IndexManager index, boolean stricted) {
		this.repository = repository;
		this.index = index;
		if (stricted) {
			searcher = new Searcher (index, true);
		} else {
			searcher = new Searcher (index);
		}
	}
	
	public Result find(QueryCriteria query, boolean copy) {
		
		Result result = new Result();
		result.setMaxResults(query.getMaxResults());
		result.setPage(query.getPage()+1);
		
		SearcherResult sresult = searcher.searchObject(query);
		if (sresult.size() > 0) {
			result.setTotalHits(sresult.getTotalHits());
			result.setMaxRange(sresult.getMaxRange());
			result.setMinRange(sresult.getMinRange());
			result.addAll(repository.getObjectsByOId(sresult.getOids()));
			return result;
		}
			
		if (sresult.size() == 0 && !canQueryRunWithIndex(query)) {
			if (repository.getConfiguration().getUseTemporalIndex()) {
				return createIndexAndFind(query, copy);
			}	
		}
		return result;
	}
	
	public Result findInMaster(QueryCriteria query, boolean copy) {
		
		Result result = new Result();
		result.setMaxResults(query.getMaxResults());
		result.setPage(query.getPage()+1);
		
		SearcherResult sresult = searcher.searchObjectInMaster(query);
		if (sresult.size() > 0) {
			result.setTotalHits(sresult.getTotalHits());
			result.setMaxRange(sresult.getMaxRange());
			result.setMinRange(sresult.getMinRange());
			result.addAll(repository.getObjectsByOId(sresult.getOids()));
			return result;
		}
		return result;
	}
	
	public Result createIndexAndFind(
			QueryCriteria query, boolean copy) {

		String qid = query.getQueryCacheId();
		String[] indexesPath = IndexUtils.getExpressionAttributesAsArray(query
				.getExpressions());
		IndexManager imanager = temporal.createIndexAndPath(query.getCriteriaClass());
		if (!imanager.hasIndexEntries()) {
			temporal.createIndex(query.getCriteriaClass(), indexesPath);
			reindex(imanager);
		}
		
		if (imanager != null) {
			
			Searcher searcher = new Searcher(imanager);
			SearcherResult sresult = searcher.searchObject(query);
			if (sresult.size() > 0) {
				Result result = new Result (true, qid);
				result.setMaxResults(query.getMaxResults());
				result.setPage(query.getPage()+1);
				result.setTotalHits(sresult.getTotalHits());
				result.setMaxRange(sresult.getMaxRange());
				result.setMinRange(sresult.getMinRange());
				result.addAll(repository.getObjectsByOId(sresult.getOids()));
				return result;
			}
			
		}
		return new Result (true, null);
	}
	
	private boolean canQueryRunWithIndex(QueryCriteria criteria) {
		List<String> criteriaAttrs = IndexUtils
				.getExpressionAttributes(criteria.getExpressions());
		if (criteriaAttrs.size() == 0)
			return false;
		for (String attr : criteriaAttrs) {
			if (!index.containsEntry(attr)) {
				return false;
			}
		}
		return true;
	}
	
	public void reindex(Class<?> clazz) throws RepositoryException {
		IndexManager imanager = temporal.getIndexManager(clazz);
		Indexer indexer = null;
		try {
			indexer = new Indexer(imanager, true, true);
			List<Tuple> tuples = repository.getAll(clazz);
			for (Tuple tuple : tuples) {
				indexer.indexObject(tuple.oid, tuple.value);
			}
		} catch (IndexException e) {
			throw new RepositoryException(e);
		} finally {
			if (indexer != null)
				indexer.close();
		}
	}

	protected void reindex(IndexManager imanager) throws RepositoryException {
		Indexer indexer = null;
		try {
			indexer = new Indexer(imanager, true, true);
			List<Tuple>tuples = repository.getAll(imanager.getIndexClass());
			for (Tuple tuple : tuples) {
				indexer.indexObject(tuple.oid, tuple.value);
			}
		} catch (IndexException e) {
			throw new RepositoryException(e);
		} finally {
			if (indexer != null)
				indexer.close();
		}
	}
	
}
